home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Periodicals / develop / develop 3 code / Surf's Up! / Surfer / Surfer.p < prev    next >
Encoding:
Text File  |  1990-05-31  |  41.2 KB  |  1,628 lines  |  [TEXT/MPS ]

  1. {------------------------------------------------------------------------------
  2. #
  3. #    Apple Products Presents
  4. #
  5. #    S U R F E R    ----- A CommToolbox Sample Application
  6. #        by Alex Kazim
  7. #
  8. #    Based on the MacDTS Simple Sample Application
  9. #
  10. #    Surfer.p    -    Pascal Source
  11. #
  12. #    Copyright © 1988-9 Apple Computer, Inc.
  13. #    All rights reserved.
  14. #
  15. #    Versions:    Sample 1.0                    08/88
  16. #                Sample 1.01                    11/88
  17. #
  18. #                Surfer 1.0                    10/89
  19. #                Surfer 1.01                    02/90
  20. ------------------------------------------------------------------------------}
  21.  
  22. {
  23. MODIFICATION HISTORY
  24.  
  25.     9/26/89        kaz        • changed case on constants to match documentation
  26.                         • Fixed error handling to only call xxEvent() if the
  27.                         target of the event is a tool window
  28.                         • Initializes gBuffer according to sizes[cmDataIn]
  29.                         after the _CMNew call
  30.                         
  31.     10/1/89        kaz        • TermGetConnEnvirons() and FTGetConnEnvirons() were
  32.                         merged into one routine: ToolGetConnEnvirons()
  33.                         • IsAppWindow() uses _GetWRefCon instead of looking
  34.                         at the windowrecord refcon field.
  35.                         • Moved DiposePtr(gBuffer) to CloseWindow()
  36.                         • _HLock/_HUnlock all the tool handles
  37.                         • Took out alerts to let the tools handle it themselves
  38.                         
  39.     10/4/89        kaz        • Was forgetting to clear gStartFT after a receive
  40.                                 
  41.     1/9/90        kaz        • Fixed a bug that didn't call _TMIdle if there was
  42.                         a same-circuit file transfer in progress.  _TMIdle
  43.                         will be called regardless.
  44.                         • TermSendProc() will not send any data if there is 
  45.                         a same-circuit file transfer in progress.
  46.                         
  47.     1/29/90        kaz        • Allocates the buffer by looking at the bufSizes in
  48.                         the ConnRecord instead of assuming my Buffer max.
  49.                         • Cleaned up TermRecvProc() to be less confusing
  50.                         
  51.     2/14/90        kaz        • Was _HLocking the term handle before _TMChoose instead
  52.                         of _HUnlocking it.
  53. }
  54.  
  55. PROGRAM Surfer;
  56.  
  57. USES
  58.     MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf,
  59.     CTBUtils, FTIntf, CMIntf, TMIntf, CRMIntf;
  60.  
  61.  
  62. CONST
  63.     _WaitNextEvent                = $A860;
  64.     _UnimplementedToolTrap        = $A89F;
  65.     _CommToolboxTrap            = $8B;
  66.     _UnimplementedOSTrap        = $9F;
  67.  
  68.     kSysEnvironsVersion        = 1;
  69.  
  70.     kOSEvent                = app4Evt;    { event used by MultiFinder    }
  71.     kSuspendResumeMessage    = 1;        { high byte of suspend/resume event message}
  72.     kResumeMask                = 1;        { bit of message field for resume vs. suspend}
  73.      
  74.     kMinHeap    = 150 * 1024; 
  75.     kMinSpace    = 10 * 1024;
  76.     kBufferSize    = 1 * 1024;                { Data Storage Size = 1K    }
  77.     
  78.     kExtremeNeg    = -32768;
  79.     kExtremePos    = 32767 - 1;            { required for old region bug    }
  80.     
  81.     kDefaultTermTool    = 'VT102 Tool';        { what tools we want first        }
  82.     kDefaultFTTool        = 'Text Tool';
  83.     kDefaultConnTool    = 'Serial Tool';
  84.     
  85.     rMenuBar    = 128;                    { application's menu bar    }
  86.     rAboutAlert    = 128;                    { about alert                }
  87.     rUserAlert    = 129;                    { error user alert            }
  88.     rWindow        = 128;                    { application's window        }
  89.  
  90.     mApple            = 128;                    {Apple menu}
  91.     iAbout            = 1;
  92.  
  93.     mFile            = 129;                    {File menu}
  94.     iNew            = 1;
  95.     iOpen            = 2;
  96.     iClose            = 4;
  97.     iSendFile        = 9;
  98.     iReceiveFile    = 10;
  99.     iQuit            = 15;
  100.  
  101.     mEdit            = 130;                    {Edit menu}
  102.     iUndo            = 1;
  103.     iCut            = 3;
  104.     iCopy            = 4;
  105.     iPaste            = 5;
  106.     iClear            = 6;
  107.  
  108.     mSettings        = 131;                    {Settings menu}
  109.     iConnection        = 1;
  110.     iFileTransfer    = 2;
  111.     iTerminal        = 3;
  112.     
  113.     kDITop        = $0050;
  114.     kDILeft        = $0070;
  115.  
  116.  
  117. VAR
  118.     gHasWaitNextEvent    : BOOLEAN;        {set up by Initialize}
  119.     gInBackground        : BOOLEAN;        {maintained by Initialize and DoEvent    }
  120.  
  121.     gStopped            : BOOLEAN;        {maintained by Initialize and SetLight    }
  122.     
  123.     gTerm                : TermHandle;    { Tool Handles: Single Session            }
  124.     gConn                : ConnHandle;
  125.     gFT                    : FTHandle;
  126.  
  127.     gBuffer                : Ptr;            { Data Storage for Reads/Writes            }
  128.     
  129.     gFTSearchRefNum        : LONGINT;        { Auto-Initiate File Transfers            }
  130.     gStartFT            : BOOLEAN;        { Auto-start                            }
  131.     gWasFT                : BOOLEAN;        { In progress                            }
  132.     
  133.     
  134. PROCEDURE AlertUser(msg: Str255; fatal: BOOLEAN); FORWARD;
  135. PROCEDURE Terminate; FORWARD;
  136.  
  137. { ******************************************************************
  138. *    TrapAvailable    - Checks to see if a given trap is implemented
  139. *
  140. *        tNumber        - trap number
  141. *        tType        - type of trap
  142. *
  143. *        returns        - true if it exists
  144. *
  145. ********************************************************************* }
  146. {$S Initialize}
  147. FUNCTION TrapAvailable(tNumber: INTEGER; tType: TrapType): BOOLEAN;
  148. VAR
  149.     unImplemented    : INTEGER;
  150.     
  151. BEGIN
  152.     IF tType = OSTrap THEN
  153.         unImplemented := _UnimplementedOSTrap
  154.     ELSE
  155.         unImplemented := _UnimplementedToolTrap;
  156.  
  157.     TrapAvailable := NGetTrapAddress(tNumber, tType) <> GetTrapAddress(unImplemented);
  158. END; {TrapAvailable}
  159.  
  160.  
  161. { ******************************************************************
  162. *    TermSendProc    - Sends the data out the connection
  163. *
  164. *        thePtr        - the data to send
  165. *        theSize        - bytes to send
  166. *        refcon        - terminal tool refcon
  167. *        flags        - connection flags
  168. *
  169. *        returns        - bytes sent
  170. *
  171. ********************************************************************* }
  172. {$S Main}
  173. FUNCTION TermSendProc(thePtr: Ptr;theSize: LONGINT;
  174.                     refcon: LONGINT;flags: INTEGER): LONGINT;
  175. VAR
  176.     theErr        : CMErr;
  177.     
  178. BEGIN
  179.     TermSendProc := 0;                        { Assume the worst            }
  180.     
  181.     IF gConn <> NIL THEN BEGIN
  182.     
  183.         { If there's a file transfer in progress AND it's     }
  184.         { over our connection, then don't send the data.    }
  185.         
  186.         IF (gFT <> NIL) THEN BEGIN
  187.             IF (BAND(gFT^^.flags, ftIsFTMode) <> 0) AND
  188.                     (BAND(gFT^^.attributes, ftSameCircuit) <> 0) THEN
  189.                 EXIT(TermSendProc);
  190.         END;
  191.         
  192.         theErr := CMWrite(gConn,thePtr,theSize,
  193.                             cmData,FALSE,NIL,0,flags);
  194.                             
  195.         IF (theErr = noErr) THEN
  196.             TermSendProc := theSize;        { If ok, we sent all        }
  197.         
  198.     END; { Good Connection    }
  199.     
  200. END; { TermSendProc        }
  201.  
  202.  
  203.  
  204. { ******************************************************************
  205. *    TermRecvProc    - Gets the data from the connection and sends 
  206. *                    it to the terminal tool.
  207. *
  208. *        NOTE        - This is NOT a callback proc, but does 
  209. *                    resemble the functionality.
  210. *
  211. *
  212. ********************************************************************* }
  213. {$S Main}
  214. PROCEDURE TermRecvProc;
  215. VAR
  216.     theErr        : CMErr;        { Any errors            }
  217.     status        : CMStatFlags;    { For the conn tool        }
  218.     sizes        : CMBufferSizes;
  219.     flags        : INTEGER;
  220.     bytesEaten    : LONGINT;        { by the terminal tool    }
  221.     
  222. BEGIN
  223.     IF (gConn <> NIL) AND (gTerm <> NIL) THEN BEGIN
  224.     
  225.         { Get the state of the connection    }
  226.         theErr := CMStatus(gConn, sizes, status);
  227.         
  228.         IF (theErr = noErr) THEN  BEGIN
  229.         
  230.             { Route the data if we have any            }
  231.             IF (BAND(status, cmStatusOpen + cmStatusDataAvail) <> 0) AND 
  232.                                     (sizes[cmDataIn] <> 0) THEN BEGIN
  233.                 
  234.                 { Tell the tool to get the data    }
  235.                 theErr := CMRead(gConn, gBuffer, sizes[cmDataIn],
  236.                                         cmData, FALSE,NIL,0,flags);
  237.                 
  238.                 { Send data to the terminal    }
  239.                 IF (theErr = noErr) THEN
  240.                     bytesEaten := TMStream(gTerm,gBuffer,
  241.                                             sizes[cmDataIn],flags);
  242.                                             
  243.                 { Could check bytesEaten vs. sizes[cmDataIn]    }
  244.                                             
  245.             END; { sizes <> 0    }
  246.                 
  247.         END; { Good Status        }
  248.         
  249.         IF (theErr <> noErr) THEN
  250.             ;        { Connection tool will alert the user on an error    }
  251.         
  252.     END; { Good term & conn    }
  253.     
  254. END; { TermRecvProc        }
  255.  
  256.  
  257.  
  258. { ******************************************************************
  259. *    ToolGetConnEnvirons    - Gets the connection environs for
  260. *                        the FT or Term tool
  261. *
  262. *        refCon            - the tool refcon
  263. *        theEnvirons        - the environment
  264. *
  265. *        returns            - an environment error
  266. *
  267. ********************************************************************* }
  268. {$S Main}
  269. FUNCTION ToolGetConnEnvirons(refCon: LONGINT;
  270.                         VAR theEnvirons: ConnEnvironRec): OSErr;
  271. BEGIN
  272.     ToolGetConnEnvirons := envNotPresent;        { pessimism         }
  273.     
  274.     { Version is set by the tool    }
  275.     IF (gConn <> NIL) THEN
  276.         ToolGetConnEnvirons := CMGetConnEnvirons(gConn,theEnvirons);
  277.     
  278. END; { TermGetConnEnvirons    }
  279.  
  280.  
  281.  
  282. { ******************************************************************
  283. *    FTSendProc    - Sends data during a file transfer
  284. *
  285. *        thePtr        - data to send
  286. *        theSize        - bytes to send
  287. *        refcon        - the FTtool refcon
  288. *        channel        - which channel to use
  289. *        flags        - connection flags
  290. *
  291. *        returns        - bytes sent
  292. *
  293. ********************************************************************* }
  294. {$S Main}
  295. FUNCTION FTSendProc(thePtr: Ptr;theSize: LONGINT;refcon: LONGINT;
  296.                         channel: CMChannel;flags: INTEGER) : LONGINT;
  297. VAR
  298.     theErr : CMErr;
  299.     
  300. BEGIN
  301.     FTSendProc := 0;                    { Assume the worst        }
  302.     
  303.     IF gConn <> NIL THEN BEGIN
  304.                                         { Send the data            }
  305.         theErr := CMWrite(gConn,thePtr,theSize,channel, 
  306.                                         FALSE, NIL, 0, flags);
  307.         IF (theErr = noErr) THEN
  308.             FTSendProc := theSize;        { if ok, we sent all    }
  309.  
  310.     END; { Good Connection    }
  311.     
  312. END; { FTSendProc    }
  313.  
  314.  
  315.  
  316. { ******************************************************************
  317. *    FTReceiveProc    - Gets data during a file transfer
  318. *
  319. *        thePtr        - place for data
  320. *        theSize        - bytes to get
  321. *        refcon        - the FTtool refcon
  322. *        channel        - which channel to use
  323. *        flags        - connection flags
  324. *
  325. *        returns        - bytes gotten
  326. *
  327. ********************************************************************* }
  328. {$S Main}
  329. FUNCTION FTReceiveProc(thePtr: Ptr;theSize: LONGINT;refcon: LONGINT; 
  330.                                 channel: CMChannel;VAR flags: INTEGER): LONGINT;
  331. VAR
  332.     theErr : CMErr;
  333.     
  334. BEGIN
  335.     FTReceiveProc := 0;                    { Assume the worst        }
  336.     
  337.     IF gConn <> NIL THEN BEGIN
  338.                                         { Read all the data        }
  339.         theErr := CMRead(gConn,thePtr,theSize, 
  340.                                         channel,FALSE,NIL,0,flags);
  341.         IF (theErr = noErr) THEN
  342.             FTReceiveProc := theSize;    { if ok, we got all        }
  343.  
  344.     END; { Good Connection    }
  345.     
  346. END; { FTReceiveProc    }
  347.  
  348.  
  349. { ******************************************************************
  350. *    AutoRecCallback    - Sets the file transfer flag if an auto-
  351. *                    receive string was found.
  352. *
  353. *        theConn            - which connection tool found it
  354. *        data            - ptr to last character in the match
  355. *        refNum            - which search was found
  356. *
  357. ********************************************************************* }
  358. {$S Main}
  359. PROCEDURE AutoRecCallback(theConn: ConnHandle; data: Ptr; refNum: LONGINT);
  360. BEGIN
  361.     { We can't call _FTStart or _CMRemoveSearch here as     }
  362.     { this proc might be called from Interrupt level        }
  363.     
  364.     IF (gFTSearchRefNum = refNum) THEN
  365.         gStartFT := TRUE;        { Set the flag to call FTStart in Idle    }
  366. END; { AutoRecCallBack    }
  367.  
  368.  
  369.  
  370. { ******************************************************************
  371. *    AddFTSearch        - Checks to see if the file transfer has an
  372. *                    auto-receive string, and adds a search to 
  373. *                    find it.
  374. *
  375. ********************************************************************* }
  376. {$S Main}
  377. PROCEDURE AddFTSearch;
  378. VAR
  379.     tempStr        : Str255;            { the string to look for    }
  380.     
  381. BEGIN    
  382.     IF (gFT <> NIL) AND (gConn <> NIL) THEN BEGIN
  383.         tempStr := gFT^^.AutoRec;        { Do I need to add a search        }
  384.         
  385.         IF (tempStr <> '') THEN BEGIN
  386.             gFTSearchRefNum := CMAddSearch(gConn,tempStr,cmSearchSevenBit,
  387.                                                             @AutoRecCallback);
  388.             IF gFTSearchRefNum = -1 THEN BEGIN
  389.                 AlertUser('Couldn''t add stream search',FALSE);
  390.                 gFTSearchRefNum := 0;
  391.             END;
  392.         END; { can autoreceive    }
  393.         
  394.     END; { good FT and Conn    }
  395.     
  396. END; { AddFTSearch        }
  397.  
  398.  
  399.  
  400. { ******************************************************************
  401. *    DoSend    - Initiates a File Transfer send from the menu command
  402. *
  403. ********************************************************************* }
  404. {$S Main}
  405. PROCEDURE DoSend;
  406. VAR
  407.     theReply    : SFReply;            { File Info                    }
  408.     where        : Point;            { Top Left of File dialog    }
  409.     numTypes    : INTEGER;            { File Types to display        }
  410.     typeList    : SFTypeList;
  411.     anyErr        : FTErr;            { Error handler                }
  412.     
  413. BEGIN
  414.     IF gFT <> NIL THEN BEGIN
  415.         SetPt(where, 100, 100);
  416.         
  417.         { If the FT tool can only send Text files, then            }
  418.         { only display text files, else display all types        }
  419.     
  420.         { Check to see if Text Only flag is set    }
  421.         IF BAND(gFT^^.attributes, ftTextOnly) <> 0 THEN BEGIN
  422.             typeList[0] := 'TEXT';
  423.             numTypes := 1;
  424.         END
  425.         ELSE
  426.             numTypes := -1;
  427.             
  428.         SFGetFile(where, 'File to Send', NIL, numTypes,
  429.                                         typeList, NIL, theReply);
  430.         
  431.         { Did the user hit OK or Cancel    }
  432.         IF theReply.good THEN BEGIN
  433.             { Transfer the file TO the remote    }
  434.             anyErr := FTStart(gFT,ftTransmitting,theReply);
  435.             
  436.             IF (anyErr <> noErr) THEN
  437.                 ;            { File Transfer tool will alert user on an error    }
  438.                 
  439.         END; { Good file    }
  440.     END; { Good FTHandle    }
  441.     
  442. END; { DoSend    }
  443.  
  444.  
  445. { ******************************************************************
  446. *    DoReceive    - Initiates a File Transfer receive from the menu
  447. *
  448. ********************************************************************* }
  449. {$S Main}
  450. PROCEDURE DoReceive;
  451. VAR
  452.     theReply        : SFReply;        { File Info            }
  453.     anyErr            : OSErr;        { Errors on Start    }
  454.     
  455. BEGIN
  456.     IF gFT <> NIL THEN BEGIN
  457.         
  458.         { Let the FT tool use its own default file info    }
  459.         theReply.vRefNum := 0;
  460.         theReply.fName := '';
  461.         theReply.good := TRUE;
  462.         
  463.         gStartFT := FALSE;        { Shut the flag down            }
  464.         
  465.         { We remove the search temporarily in case it comes        }
  466.         { across during the transfer. Will be re-added in the    }
  467.         { idle loop once the transfer is completed                }
  468.         
  469.         IF gConn <> NIL THEN
  470.             IF (gFT^^.autoRec <> '') AND (gFTSearchRefNum <> 0) THEN BEGIN
  471.                 CMRemoveSearch(gConn, gFTSearchRefNum);
  472.                 gFTSearchRefNum := 0;    { We found it already    }
  473.             END;
  474.         
  475.         { Start receiving the file                        }
  476.         { The rest gets transferred in the Idle loop    }
  477.         
  478.         anyErr := FTStart(gFT,ftReceiving,theReply);
  479.         
  480.         IF (anyErr <> noErr) THEN
  481.             ;            { File Transfer tool will alert user on an error    }
  482.             
  483.     END; { Good Handle    }
  484.     
  485. END; { DoReceive    }
  486.  
  487.  
  488. { ******************************************************************
  489. *    IsDAWindow    - Checks to see if a window belongs to a desk acc.
  490. *
  491. *        window        - the culprit
  492. *
  493. *        returns        - true if it's a DA
  494. *
  495. ********************************************************************* }
  496. {$S Main}
  497. FUNCTION IsDAWindow(window: WindowPtr): BOOLEAN;
  498. BEGIN
  499.     IF window = NIL THEN
  500.         IsDAWindow := FALSE
  501.     ELSE    {DA windows have negative windowKinds}
  502.         IsDAWindow := WindowPeek(window)^.windowKind < 0;
  503. END; {IsDAWindow}
  504.  
  505.  
  506.  
  507. { ******************************************************************
  508. *    IsAppWindow    - Checks to see if a window belongs to our app
  509. *
  510. *        window        - the culprit
  511. *
  512. *        returns        - true if it's an app window
  513. *
  514. ********************************************************************* }
  515. {$S Main}
  516. FUNCTION IsAppWindow(window: WindowPtr): BOOLEAN;
  517. VAR
  518.     theRefCon        : LONGINT;
  519.     
  520. BEGIN
  521.     { Check the userkind and the refcon for tool windows}
  522.     IF window = NIL THEN
  523.         IsAppWindow := FALSE
  524.     ELSE BEGIN
  525.         theRefCon := GetWRefCon(window);
  526.         WITH WindowPeek(window)^ DO
  527.             IsAppWindow := ((windowKind >= userKind) | (windowKind = dialogKind)) &
  528.                                 (gTerm <> TermHandle(theRefCon)) &
  529.                                 (gConn <> ConnHandle(theRefCon)) &
  530.                                 (gFT <> FTHandle(theRefCon));
  531.     END;
  532. END; {IsAppWindow}
  533.  
  534.  
  535.  
  536. { ******************************************************************
  537. *    AlertUser    - Informs the user of any errors
  538. *
  539. *        msg        - The string to display
  540. *        fatal    - Exit if this is a fatal error
  541. *
  542. ********************************************************************* }
  543. {$S Main}
  544. PROCEDURE AlertUser(msg: Str255; fatal: BOOLEAN); 
  545. VAR
  546.     itemHit    : INTEGER;
  547.     
  548. BEGIN
  549.     SetCursor(arrow);
  550.     
  551.     ParamText(msg,'','','');
  552.     itemHit := Alert(rUserAlert, NIL);
  553.     
  554.     IF fatal THEN
  555.         Terminate;
  556. END; { AlertUser    }
  557.  
  558.  
  559.  
  560. { ******************************************************************
  561. *    OpenConnection    - Initiates a connection
  562. *
  563. ********************************************************************* }
  564. {$S Main}
  565. PROCEDURE OpenConnection; 
  566. VAR
  567.     theErr    : CMErr;
  568.     sizes    : CMBufferSizes;        { Connection Tool data        }
  569.     status    : CMStatFlags;
  570.     
  571. BEGIN
  572.     IF (gConn <> NIL) THEN BEGIN
  573.     
  574.         { Get connection info        }
  575.         theErr := CMStatus(gConn, sizes, status);
  576.         
  577.         IF (theErr = noErr) THEN BEGIN
  578.             { If it isn't already open, then open it    }
  579.             IF BAND(status, cmStatusOpen + cmStatusOpening) = 0 THEN
  580.                 theErr := CMOpen(gConn, FALSE, NIL, 0);
  581.         END;
  582.         
  583.         IF (theErr <> noErr) THEN
  584.             ;            { Conn tool will alert user on an error        }
  585.     END;
  586. END; {OpenConnection}
  587.  
  588.  
  589.  
  590. { ******************************************************************
  591. *    CloseConnection    - Kills a connection
  592. *
  593. ********************************************************************* }
  594. {$S Main}
  595. PROCEDURE CloseConnection; 
  596. VAR
  597.     theErr    : CMErr;
  598.     sizes    : CMBufferSizes;        { Connection Tool data        }
  599.     status    : CMStatFlags;
  600.     
  601. BEGIN
  602.     { Kill the current connection    }
  603.     IF (gConn <> NIL) THEN BEGIN
  604.         theErr := CMStatus(gConn, sizes, status);
  605.         
  606.         { If it's open, then close it    }
  607.         IF (theErr = noErr) THEN
  608.             IF BAND(status, cmStatusOpen + cmStatusOpening) <> 0 THEN
  609.                 theErr := CMClose(gConn, FALSE, NIL, 0, TRUE);
  610.         
  611.         IF (theErr <> noErr) THEN
  612.             ;        { Conn tool will alert user on an error        }
  613.  
  614.     END;
  615.     
  616. END; {CloseConnection}
  617.  
  618.  
  619.  
  620. { ******************************************************************
  621. *    DoCloseWindow    - Closes the window
  622. *
  623. *        window        - the culprit
  624. *
  625. *        returns        - always returns true
  626. *
  627. ********************************************************************* }
  628. {$S Main}
  629. FUNCTION DoCloseWindow(window: WindowPtr): BOOLEAN;
  630. BEGIN
  631.     DoCloseWindow := TRUE;
  632.     
  633.     IF IsDAWindow(window) THEN
  634.         CloseDeskAcc(WindowPeek(window)^.windowKind)
  635.     ELSE IF IsAppWindow(window) THEN BEGIN
  636.     
  637.         CloseConnection;            { Stop what we're doin'        }
  638.         
  639.         IF gTerm <> NIL THEN BEGIN    { Dispose of all the tools    }
  640.             HUnlock(Handle(gTerm));
  641.             TMDispose(gTerm);
  642.         END;
  643.         
  644.         IF gFT <> NIL THEN BEGIN
  645.             HUnlock(Handle(gFT));
  646.             FTDispose(gFT);
  647.         END;
  648.             
  649.         IF (gConn <> NIL) THEN BEGIN
  650.             HUnlock(Handle(gConn));
  651.             CMDispose(gConn);
  652.         END;
  653.                 
  654.         IF (gBuffer <> NIL) THEN    { Clean up our buffer        }
  655.             DisposPtr(gBuffer);
  656.             
  657.         DisposeWindow(window);
  658.     END; { App Window    }
  659.     
  660. END; {DoCloseWindow}
  661.  
  662.  
  663. { ******************************************************************
  664. *    FindToolID    - Tries to get the default tool proc id,
  665. *                otherwise, gets the first one it finds.
  666. *
  667. *        toolClass    - What kind of tool: term, ft, conn
  668. *
  669. *        returns        - the tool proc id or -1 if not found
  670. *
  671. ********************************************************************* }
  672. {$S Main}
  673. FUNCTION FindToolID(toolClass: OSType): INTEGER;
  674. VAR
  675.     toolName    : Str255;        { tool file name        }
  676.     anyErr        : OSErr;
  677.     procID        : INTEGER;        { tool fref number        }
  678.     
  679. BEGIN
  680.     procID := -1;                { Unknown tool            }
  681.     
  682.     IF (toolClass = ClassTM) THEN BEGIN
  683.         { If it can't get the default, get the 1st    }
  684.         toolName := kDefaultTermTool;    {VT102 Tool}
  685.         procID := TMGetProcID(toolName);
  686.         
  687.         IF (procID = -1) THEN BEGIN
  688.             anyErr := CRMGetIndToolName(toolClass,1,toolName);
  689.             IF (anyErr = noErr) THEN
  690.                 procID := TMGetProcID(toolName);
  691.         END;
  692.         
  693.     END { ClassTM}
  694.     
  695.     ELSE IF (toolClass = ClassCM) THEN BEGIN
  696.         { If it can't get the default, get the 1st    }
  697.         toolName := kDefaultConnTool;
  698.         procID := CMGetProcID(toolName);
  699.         
  700.         IF (procID = -1) THEN BEGIN
  701.             anyErr := CRMGetIndToolName(toolClass,1,toolName);
  702.             IF (anyErr = noErr) THEN
  703.                 procID := CMGetProcID(toolName);
  704.         END;
  705.         
  706.     END { ClassCM}
  707.         
  708.     ELSE IF (toolClass = ClassFT) THEN BEGIN
  709.         { If it can't get the default, get the 1st    }
  710.         toolName := kDefaultFTTool;
  711.         procID := FTGetProcID(toolName);
  712.         
  713.         IF (procID = -1) THEN BEGIN
  714.             anyErr := CRMGetIndToolName(toolClass,1,toolName);
  715.             IF (anyErr = noErr) THEN
  716.                 procID := FTGetProcID(toolName);
  717.         END;
  718.         
  719.     END; { ClassFT}
  720.     
  721.     FindToolID := procID;
  722.         
  723. END; {FindToolID}
  724.  
  725.  
  726. { ******************************************************************
  727. *    DoNewWindow    - Gets the window and creates the session
  728. *
  729. *        window        - the culprit
  730. *
  731. *        returns        - always returns true
  732. *
  733. ********************************************************************* }
  734. {$S Main}
  735. FUNCTION DoNewWindow: BOOLEAN;
  736. VAR
  737.     window        : WindowPtr;    { the window to create            }
  738.     theRect        : Rect;            { for the terminal bounds        }
  739.     procID        : INTEGER;        { tool's ref number                }
  740.     sizes        : CMBufferSizes;{ requested size of the buffers    }
  741.     
  742. BEGIN
  743.     { Get window                        }
  744.     window := GetNewWindow(rWindow, NIL, WindowPtr(-1));
  745.     SetPort(window);    
  746.     
  747.     { TERMINAL TOOL    }
  748.     procID := FindToolID(ClassTM);
  749.     IF (procID = -1) THEN
  750.         AlertUser('No terminal tools found',TRUE);
  751.         
  752.     theRect := window^.portRect;
  753.     
  754.     { Flags set to 0; no cacheProc, breakProc, or clikLoop;     }
  755.     { refCon and UserData are 0.                            }
  756.     gTerm := TMNew(theRect,theRect,0,procID,window, 
  757.                 @TermSendProc,NIL,NIL,NIL,@ToolGetConnEnvirons,0,0);
  758.     IF gTerm = NIL THEN
  759.         AlertUser('Can''t create a terminal tool',TRUE);
  760.     
  761.     MoveHHi(Handle(gTerm));
  762.     HLock(Handle(gTerm));
  763.     
  764.     { CONNECTION TOOL    }
  765.     procID := FindToolID(ClassCM);
  766.     IF (procID = -1) THEN
  767.         AlertUser('No connection tools found',TRUE);
  768.     
  769.     sizes[cmDataIn] := kBufferSize;
  770.     sizes[cmDataOut] := kBufferSize;
  771.     sizes[cmCntlIn] := 0;
  772.     sizes[cmCntlOut] := 0;
  773.     sizes[cmAttnIn] := 0;
  774.     sizes[cmAttnOut] := 0;
  775.     
  776.     { refCon and UserData are 0. }
  777.     gConn := CMNew(procID, cmData, sizes, 0, 0);
  778.     IF gConn = NIL THEN
  779.         AlertUser('Can''t create a connection tool',TRUE);
  780.         
  781.     MoveHHi(Handle(gConn));
  782.     HLock(Handle(gConn));
  783.     
  784.     { Allocate space for the read/writes using the number    }
  785.     { returned by the connection tool                        }
  786.     gBuffer := NewPtr(gConn^^.bufSizes[cmDataIn]);
  787.     IF MemError <> noErr THEN
  788.         AlertUser('Out of memory',TRUE);
  789.     
  790.     { FILE TRANSFER TOOL    }
  791.     procID := FindToolID(ClassFT);
  792.     IF (procID = -1) THEN
  793.         AlertUser('No file transfer tools found',FALSE);
  794.     
  795.     { Flags set to 0, no read/write proc (let the tool use its own),}
  796.     { refCon and UserData are 0.                                     }
  797.     gFT := FTNew(procID,0,@FTsendProc,@FTreceiveProc,NIL,NIL,
  798.                     @ToolGetConnEnvirons,window,0,0);
  799.     IF gFT = NIL THEN
  800.         AlertUser('Can''t create a file transfer tool',TRUE);
  801.         
  802.     MoveHHi(Handle(gFT));
  803.     HLock(Handle(gFT));
  804.     
  805.     gWasFT := FALSE;            { FT in progress                    }
  806.     gStartFT := FALSE;            { Auto-received string found        }
  807.     gFTSearchRefNum := 0;        { Clear the search refnum            }
  808.                 
  809.     AddFTSearch;                { Look for the auto-receive string    }
  810.     
  811.     DoNewWindow := TRUE;
  812.                         
  813. END; {DoNewWindow}
  814.  
  815.  
  816.  
  817. { ******************************************************************
  818. *    Initialize        - Inits the various toolbox stuff
  819. *
  820. ********************************************************************* }
  821.  
  822. {$S Initialize}
  823. PROCEDURE Initialize;
  824.  
  825. VAR
  826.     menuBar            : Handle;
  827.     window            : WindowPtr;
  828.     ignoreError        : OSErr;
  829.     total, contig    : LongInt;
  830.     ignoreResult    : BOOLEAN;
  831.     event            : EventRecord;
  832.     count            : INTEGER;
  833.     TerraMac        : SysEnvRec;    {set up by Initialize}
  834.     err                : INTEGER;
  835.  
  836. BEGIN
  837.     { Do we have Multifinder?    }
  838.     gHasWaitNextEvent := TrapAvailable(_WaitNextEvent, ToolTrap);
  839.     gInBackground := FALSE;
  840.  
  841.     { Standard Fare        }
  842.     InitGraf(@thePort);
  843.     InitFonts;
  844.     InitWindows;
  845.     InitMenus;
  846.     TEInit;
  847.     InitDialogs(NIL);
  848.     InitCursor;
  849.     
  850.     { Bring us to the front        }
  851.     FOR count := 1 TO 3 DO
  852.         ignoreResult := GetNextEvent(everyEvent, event);
  853.     
  854.     { Does CommToolbox Exist?    }
  855.     IF NOT TrapAvailable(_CommToolboxTrap, OSTrap) THEN
  856.         AlertUser('ACK!! No CommToolbox',TRUE);
  857.     
  858.     { Check for System 6.0 or better, 64K ROM    }
  859.     ignoreError := SysEnvirons(kSysEnvironsVersion, TerraMac);
  860.     
  861.     WITH TerraMac DO
  862.         IF (systemVersion < $0600) OR (machineType < 0) THEN 
  863.             AlertUser('Need System 6.0 or better',TRUE);
  864.      
  865.     { Check various memory configs                }
  866.     IF ORD(GetApplLimit) - ORD(ApplicZone) < kMinHeap THEN 
  867.         AlertUser('Out of Memory',TRUE);
  868.     
  869.     PurgeSpace(total, contig);
  870.     IF total < kMinSpace THEN 
  871.         AlertUser('Out of Memory',TRUE);
  872.     
  873.     { Load up the Communications Toolbox        }
  874.     { Must Initialize CRM & CTBUtilities first    }
  875.     err :=     InitCTBUtilities;
  876.     err :=     InitCRM;
  877.  
  878.     err := InitTM;    { initializes the Terminal Manager }
  879.     IF err = TMNoTools THEN
  880.         AlertUser('No terminal tools found',TRUE);
  881.     
  882.     err :=     InitCM;    { initializes the Connection Manager }
  883.     IF err = CMNoTools THEN
  884.         AlertUser('No connection tools found',TRUE);
  885.     
  886.     err :=     InitFT;    { initializes the File Transfer Manager }
  887.     IF err = FTNoTools THEN
  888.         AlertUser('No file transfer tools found',FALSE);
  889.         
  890.     gTerm := NIL;
  891.     gConn := NIL;
  892.     gFT := NIL;
  893.     gFTSearchRefNum := 0;
  894.     
  895.     IF NOT DoNewWindow THEN
  896.         AlertUser('Can''t create a session',TRUE);
  897.  
  898.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  899.     IF menuBar = NIL THEN 
  900.         AlertUser('Can''t get the menu bar',TRUE);
  901.     SetMenuBar(menuBar);                    {install menus}
  902.     DisposHandle(menuBar);
  903.     
  904.     AddResMenu(GetMHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  905.     DrawMenuBar;
  906.  
  907.     gStopped := TRUE;
  908. END; {Initialize}
  909.  
  910.  
  911. { ******************************************************************
  912. *    Terminate        - Cleans up and exits
  913. *
  914. ********************************************************************* }
  915.  
  916. {$S Main}
  917. PROCEDURE Terminate;
  918. VAR
  919.     aWindow    : WindowPtr;        { the window to shut        }
  920.     closed    : BOOLEAN;            { Are we done, yet            }
  921.  
  922. BEGIN
  923.     { Close all the open windows    }
  924.     closed := TRUE;
  925.  
  926.     aWindow := FrontWindow;    
  927.     
  928.     REPEAT        
  929.         IF (aWindow <> NIL) THEN
  930.             IF IsAppWindow(aWindow) THEN
  931.                 closed := DoCloseWindow(aWindow);
  932.                 
  933.         { Try the next window    }
  934.         IF (aWindow <> NIL) THEN
  935.             aWindow := WindowPtr(WindowPeek(aWindow)^.nextWindow); 
  936.         
  937.     UNTIL (NOT closed) | (aWindow = NIL);
  938.     
  939.     IF closed THEN
  940.         ExitToShell;                            {exit if no cancellation}
  941. END; {Terminate}
  942.  
  943.  
  944.  
  945. { ******************************************************************
  946. *    AdjustMenus        - Enables & Disables items based on current state
  947. *
  948. ********************************************************************* }
  949.  
  950. {$S Main}
  951. PROCEDURE AdjustMenus;
  952. VAR
  953.     window            : WindowPtr;        { whose in front            }
  954.     menu            : MenuHandle;        { the menu to manipulate    }
  955.     theErr            : CMErr;
  956.     sizes            : CMBufferSizes;        { Connection tool data        }
  957.     status            : CMStatFlags;
  958.  
  959. BEGIN
  960.     window := FrontWindow;
  961.  
  962.     menu := GetMHandle(mFile);
  963.     IF (menu = NIL) THEN
  964.         AlertUser('Can''t get menu resource', TRUE);
  965.         
  966.     IF (gConn <> NIL) THEN BEGIN
  967.         theErr := CMStatus(gConn,sizes,status);
  968.         IF (theErr = noErr) THEN BEGIN
  969.             IF NOT IsDAWindow(window) THEN BEGIN
  970.                 SetItem(menu,iOpen,'Open Connection');
  971.                 SetItem(menu,iClose,'Close Connection');
  972.                 
  973.                 { Let the menu show the proper state of the union    }
  974.                 IF BAND(status, cmStatusOpen + cmStatusOpening) = 0 THEN BEGIN
  975.                     EnableItem(menu, iOpen);
  976.                     DisableItem(menu, iClose);
  977.                 END
  978.                 ELSE BEGIN
  979.                     DisableItem(menu, iOpen);
  980.                     EnableItem(menu, iClose);
  981.                 END;
  982.                 
  983.                 { Check state of the FT tool to Enable send/receive    }
  984.                 DisableItem(menu,iSendFile);
  985.                 DisableItem(menu,iReceiveFile);
  986.                 
  987.                 IF (gFT <> NIL) THEN BEGIN
  988.                     IF BAND(gFT^^.attributes,ftSendDisable) = 0 THEN
  989.                         EnableItem(menu,iSendFile);
  990.                         
  991.                     IF BAND(gFT^^.attributes,ftReceiveDisable) = 0 THEN
  992.                         EnableItem(menu,iReceiveFile);                    
  993.                 END;
  994.             END
  995.             ELSE BEGIN
  996.                 { Set for desk accesories    }
  997.                 SetItem(menu,iOpen,'Open');
  998.                 SetItem(menu,iClose,'Close');
  999.                 DisableItem(menu, iOpen);
  1000.                 EnableItem(menu,iClose);
  1001.                 DisableItem(menu,iSendFile);
  1002.                 DisableItem(menu,iReceiveFile);
  1003.             END;
  1004.             
  1005.         END; { good status    }
  1006.     END; { good connection    }
  1007.     
  1008.     
  1009.     menu := GetMHandle(mEdit);
  1010.     IF (menu = NIL) THEN
  1011.         AlertUser('Can''t get menu resource', TRUE);
  1012.     
  1013.     IF IsDAWindow(window) THEN BEGIN        { DAs might use this menu    }
  1014.         EnableItem(menu, iUndo);
  1015.         EnableItem(menu, iCut);
  1016.         EnableItem(menu, iCopy);
  1017.         EnableItem(menu, iPaste);
  1018.         EnableItem(menu, iClear);
  1019.     END ELSE BEGIN                            { but we don't use it yet    }
  1020.         DisableItem(menu, iUndo);
  1021.         DisableItem(menu, iCut);
  1022.         DisableItem(menu, iCopy);
  1023.         DisableItem(menu, iClear);
  1024.         DisableItem(menu, iPaste);
  1025.     END;
  1026.  
  1027.     menu := GetMHandle(mSettings);
  1028.     IF (menu = NIL) THEN
  1029.         AlertUser('Can''t get menu resource', TRUE);
  1030.         
  1031.     IF NOT IsDAWindow(window) THEN BEGIN        { Enable if we're front    }
  1032.         EnableItem(menu, iConnection);
  1033.         EnableItem(menu, iFileTransfer);
  1034.         EnableItem(menu, iTerminal);
  1035.     END ELSE BEGIN
  1036.         DisableItem(menu, iConnection);
  1037.         DisableItem(menu, iFileTransfer);
  1038.         DisableItem(menu, iTerminal);
  1039.     END;
  1040.     
  1041. END; {AdjustMenus}
  1042.  
  1043.  
  1044. { ******************************************************************
  1045. *    DoToolMenu        - Tries to give the menu to the tool
  1046. *
  1047. *        menuID        - the menu info from DoMenuCommand
  1048. *        menuItem
  1049. *
  1050. *        returns        - TRUE if a tool handled the menu
  1051. *
  1052. ********************************************************************* }
  1053. {$S Main}
  1054. FUNCTION DoToolMenu(menuID, menuItem: INTEGER): BOOLEAN;
  1055. BEGIN
  1056.     DoToolMenu := FALSE;
  1057.     
  1058.     IF gTerm <> NIL THEN
  1059.         IF TMMenu(gTerm, menuID, menuItem) THEN BEGIN
  1060.             DoToolMenu := TRUE;
  1061.             Exit(DoToolMenu);
  1062.         END;
  1063.  
  1064.     IF gConn <> NIL THEN
  1065.         IF CMMenu(gConn, menuID, menuItem) THEN BEGIN
  1066.             DoToolMenu := TRUE;
  1067.             Exit(DoToolMenu);
  1068.         END;
  1069.     
  1070.     IF gFT <> NIL THEN
  1071.         IF FTMenu(gFT, menuID, menuItem) THEN
  1072.             DoToolMenu := TRUE;
  1073.             
  1074. END; {DoToolMenu}
  1075.  
  1076.  
  1077. { ******************************************************************
  1078. *    DoMenuCommand    - Executes a menu command
  1079. *
  1080. *        menuResult    - the menu id and item number
  1081. *
  1082. ********************************************************************* }
  1083.  
  1084. {$S Main}
  1085. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  1086. VAR
  1087.     menuID            : INTEGER;        { resource ID of the selected menu    }
  1088.     menuItem        : INTEGER;        { item number of the selected menu    }
  1089.     itemHit            : INTEGER;        { for the alert                        }
  1090.     daName            : Str255;        { for opening desk accesories        }
  1091.     daRefNum        : INTEGER;
  1092.     handledByDA        : BOOLEAN;        { DA edit menu handling                }
  1093.     ignore            : BOOLEAN;        
  1094.     where            : Point;        { For choose dialog                    }
  1095.     result            : INTEGER;
  1096.  
  1097. BEGIN
  1098.     menuID := HiWrd(menuResult);    {use built-ins (for efficiency)...}
  1099.     menuItem := LoWrd(menuResult);    {to get menu item number and menu number}
  1100.     
  1101.     { First see if the menu belonged to a tool    }
  1102.         
  1103.     IF NOT DoToolMenu(menuID,menuItem) THEN
  1104.         CASE menuID OF
  1105.             mApple:
  1106.                 CASE menuItem OF
  1107.                     iAbout:                {bring up alert for About}
  1108.                         itemHit := Alert(rAboutAlert, NIL);
  1109.                     OTHERWISE BEGIN        {all non-About items in this menu are DAs}
  1110.                         GetItem(GetMHandle(mApple), menuItem, daName);
  1111.                         daRefNum := OpenDeskAcc(daName);
  1112.                     END;
  1113.                 END; { case    }
  1114.                 
  1115.             mFile:
  1116.                 CASE menuItem OF
  1117.                     iOpen:
  1118.                         IF NOT IsDAWindow(FrontWindow) THEN
  1119.                             OpenConnection;
  1120.                         
  1121.                     iClose:
  1122.                         IF IsDAWindow(FrontWindow) THEN
  1123.                             ignore := DoCloseWindow(FrontWindow)
  1124.                         ELSE
  1125.                             CloseConnection;
  1126.                             
  1127.                     iSendFile:
  1128.                         IF NOT IsDAWindow(FrontWindow) THEN
  1129.                             DoSend;
  1130.                         
  1131.                     iReceiveFile:
  1132.                         IF NOT IsDAWindow(FrontWindow) THEN
  1133.                             DoReceive;
  1134.                             
  1135.                     iQuit:
  1136.                         Terminate;
  1137.                 END; { case    }
  1138.                 
  1139.             mEdit:                        {call SystemEdit for DA editing & MultiFinder}
  1140.                 handledByDA := SystemEdit(menuItem-1);    {since we don't do any editing}
  1141.                 
  1142.             mSettings:
  1143.                 CASE menuItem OF
  1144.                     iConnection:
  1145.                         IF gConn <> NIL THEN BEGIN
  1146.                             HUnlock(Handle(gConn));
  1147.                             
  1148.                             SetPt(where,10,40);
  1149.  
  1150.                             result := CMChoose(gConn, where, NIL);
  1151.  
  1152.                             CASE result OF
  1153.                                 chooseDisaster,
  1154.                                 chooseFailed:
  1155.                                     AlertUser('Connection choose failed',(result = chooseDisaster));
  1156.                                 chooseOKMajor: BEGIN
  1157.                                     AddFTSearch;
  1158.                                 END;
  1159.                             END;
  1160.                             
  1161.                             HLock(Handle(gConn));
  1162.                         END; { good conn    }
  1163.                         
  1164.                     iFileTransfer:
  1165.                         IF (gFT <> NIL) THEN BEGIN
  1166.                             HUnlock(Handle(gFT));
  1167.                             
  1168.                             SetPt(where,10,40);
  1169.                             result := FTChoose(gFT, where, NIL);
  1170.                             
  1171.                             CASE result OF
  1172.                                 chooseDisaster,
  1173.                                 chooseFailed:
  1174.                                     AlertUser('File Transfer choose failed',
  1175.                                                             (result = chooseDisaster));
  1176.                                 chooseOKMinor,
  1177.                                 chooseOKMajor: BEGIN
  1178.                                     { Get rid of the old search            }
  1179.                                     IF (gFTSearchRefNum <> 0) AND (gConn <> NIL) THEN
  1180.                                         CMRemoveSearch(gConn,gFTSearchRefNum);
  1181.                                     gFTSearchRefNum := 0;
  1182.                                     
  1183.                                     AddFTSearch;    { Add the new FT tool search    }
  1184.                                 END;
  1185.                             END;
  1186.                             
  1187.                             HLock(Handle(gFT));
  1188.                         END; { good ft    }
  1189.                         
  1190.                     iTerminal: 
  1191.                         IF (gTerm <> NIL) THEN BEGIN
  1192.                             HUnlock(Handle(gTerm));
  1193.                             
  1194.                             SetPt(where,10,40);
  1195.                             result := TMChoose(gTerm, where, NIL);
  1196.                             
  1197.                             IF (result < 0) THEN
  1198.                                 AlertUser('Terminal choose failed',(result = chooseDisaster));
  1199.                             
  1200.                             HLock(Handle(gTerm));
  1201.                         END; { good term    }
  1202.                         
  1203.                 END; { case    menuitem    }
  1204.                     
  1205.         END; { case menuid    }
  1206.     
  1207.     HiliteMenu(0);                    {unhighlight what MenuSelect (or MenuKey) hilited}
  1208. END; {DoMenuCommand}
  1209.  
  1210.  
  1211. { ******************************************************************
  1212. *    DoUpdate    - Updates the window
  1213. *
  1214. *        window    - target of teh update
  1215. *
  1216. ********************************************************************* }
  1217. {$S Main}
  1218. PROCEDURE DoUpdate(window: WindowPtr);
  1219. VAR
  1220.     savedClip    : RgnHandle;        { saved info for reset later    }
  1221.     savedPort    : GrafPtr;
  1222.     
  1223. BEGIN
  1224.     IF IsAppWindow(window) THEN BEGIN
  1225.         GetPort(savedPort);
  1226.         SetPort(window);
  1227.         
  1228.         { Clip to the window content    }
  1229.         savedClip := NewRgn;
  1230.         GetClip(savedClip);
  1231.         ClipRect(window^.portRect);
  1232.         
  1233.         BeginUpdate(window);
  1234.             IF gTerm <> NIL THEN    { Update the terminal tool        }
  1235.                 TMUpdate(gTerm, window^.visRgn);
  1236.         EndUpdate(window);
  1237.         
  1238.         SetClip(savedClip);
  1239.         DisposeRgn(savedClip);
  1240.         
  1241.         SetPort(savedPort);
  1242.     END;
  1243. END; {DoUpdate}
  1244.  
  1245.  
  1246. { ******************************************************************
  1247. *    DoResume    - Suspends/Resumes the window
  1248. *
  1249. *        becomingActive    - Resume or Suspend
  1250. *
  1251. ********************************************************************* }
  1252.  
  1253. {$S Main}
  1254. PROCEDURE DoResume(becomingActive: BOOLEAN);
  1255. VAR
  1256.     theWindow    : WindowPtr;
  1257.     savedPort    : GrafPtr;
  1258.     
  1259. BEGIN
  1260.     { Since the front window could be a tool window, we need    }
  1261.     { to find the app window by walking the list so we can        }
  1262.     { send resume messages to the tools                            }
  1263.     
  1264.     GetPort(savedPort);
  1265.     
  1266.     theWindow := FrontWindow;
  1267.     
  1268.     WHILE (theWindow <> NIL) DO BEGIN
  1269.         IF IsAppWindow(theWindow) THEN BEGIN
  1270.             SetPort(theWindow);
  1271.         
  1272.             { Tools need to adjust their menus, text selection, etc    }            
  1273.             IF gTerm <> NIL THEN
  1274.                 TMResume(gTerm, becomingActive);
  1275.             
  1276.             IF gConn <> NIL THEN
  1277.                 CMResume(gConn, becomingActive);
  1278.         
  1279.             IF gFT <> NIL THEN
  1280.                 FTResume(gFT, becomingActive);
  1281.         END; { app window    }
  1282.         
  1283.         { Try the next window    }
  1284.         theWindow := WindowPtr(WindowPeek(theWindow)^.nextWindow);
  1285.     END;
  1286.     
  1287.     SetPort(savedPort);
  1288.     
  1289. END; {DoResume}
  1290.  
  1291.  
  1292. { ******************************************************************
  1293. *    DoActivate    - (De)Activates the window
  1294. *
  1295. *        window            - target of the update
  1296. *        becomingActive    - Activate or Deactivate
  1297. *
  1298. ********************************************************************* }
  1299.  
  1300. {$S Main}
  1301. PROCEDURE DoActivate(window: WindowPtr; becomingActive: BOOLEAN);
  1302. BEGIN
  1303.     IF IsAppWindow(window) THEN BEGIN
  1304.         SetPort(window);
  1305.     
  1306.         { Tools need to adjust their menus, text selection, etc    }            
  1307.         IF gTerm <> NIL THEN
  1308.             TMActivate(gTerm, becomingActive);
  1309.         
  1310.         IF gConn <> NIL THEN
  1311.             CMActivate(gConn, becomingActive);
  1312.     
  1313.         IF gFT <> NIL THEN
  1314.             FTActivate(gFT, becomingActive);
  1315.     END;
  1316.     
  1317. END; {DoActivate}
  1318.  
  1319.  
  1320. { ******************************************************************
  1321. *    AdjustCursor    - Updates mouse cursor depending on location
  1322. *
  1323. *        mouse        - the location of the mouse (global coords)
  1324. *
  1325. ********************************************************************* }
  1326. {$S Main}
  1327. PROCEDURE AdjustCursor(mouse: Point);
  1328. VAR
  1329.     window                : WindowPtr;
  1330.  
  1331. BEGIN
  1332.     window := FrontWindow;        { Adjust only if front            }
  1333.     
  1334.     IF (NOT gInBackground) AND (IsAppWindow(window)) THEN BEGIN
  1335.         GlobalToLocal(mouse);
  1336.         
  1337.         { If it's outside the content, set to arrow        }
  1338.         { otherwise the terminal tool will  handle it    }
  1339.         
  1340.         IF (gTerm <> NIL) THEN
  1341.             IF NOT PtInRect(mouse,gTerm^^.viewRect) THEN
  1342.                 InitCursor;
  1343.                 
  1344.     END; { app window    }
  1345.     
  1346. END; {AdjustCursor}
  1347.  
  1348.  
  1349. { ******************************************************************
  1350. *    DoToolEvent        - Tries to pass the event to a tool if the 
  1351. *                    window is a tool window
  1352. *
  1353. *        event        - the event received
  1354. *
  1355. *        returns        - True if the tool handled it
  1356. *
  1357. ********************************************************************* }
  1358. {$S Main}
  1359. FUNCTION DoToolEvent(event: EventRecord; window: WindowPtr): BOOLEAN;
  1360. BEGIN
  1361.     IF (window <> NIL) THEN BEGIN
  1362.         DoToolEvent := TRUE;
  1363.         
  1364.         IF (gFT <> NIL) AND 
  1365.                 (gFT = FTHandle(GetWRefCon(window))) THEN
  1366.             FTEvent(gFT, event)
  1367.         ELSE IF (gConn <> NIL) AND 
  1368.                 (gConn = ConnHandle(GetWRefCon(window))) THEN
  1369.             CMEvent(gConn, event)
  1370.         ELSE IF (gTerm <> NIL) AND 
  1371.                 (gTerm = TermHandle(GetWRefCon(window))) THEN
  1372.             TMEvent(gTerm, event)
  1373.         ELSE
  1374.             DoToolEvent := FALSE;
  1375.     END
  1376.     ELSE
  1377.         DoToolEvent := FALSE;
  1378.         
  1379. END; {DoToolEvent}
  1380.  
  1381.  
  1382.  
  1383. { ******************************************************************
  1384. *    DoEvent    - Updates mouse cursor depending on location
  1385. *
  1386. *        event    - the event to handle
  1387. *
  1388. ********************************************************************* }
  1389. {$S Main}
  1390. PROCEDURE DoEvent(event: EventRecord);
  1391. VAR
  1392.     part,                            { where the mouse click was    }
  1393.     err            : INTEGER;
  1394.     window        : WindowPtr;        { the click's window        }
  1395.     key            : CHAR;                { the letter typed            }
  1396.     aPoint        : Point;            { for the dialog top left    }
  1397.     result        : LONGINT;            { result from MenuKey        }
  1398.     processed    : BOOLEAN;            { Did the App handle it        }
  1399.  
  1400. BEGIN
  1401.     CASE event.what OF
  1402.         mouseDown: BEGIN
  1403.             part := FindWindow(event.where, window);
  1404.  
  1405.             CASE part OF
  1406.                 inMenuBar: BEGIN            {process the menu command}
  1407.                     AdjustMenus;
  1408.                     DoMenuCommand(MenuSelect(event.where));
  1409.                 END;
  1410.                 
  1411.                 inSysWindow:                {let the system handle the mouseDown}
  1412.                     SystemClick(event, window);
  1413.                     
  1414.                 inContent:
  1415.                     { The terminal tool needs to handle selections    }
  1416.                     IF NOT DoToolEvent(event,window) THEN BEGIN
  1417.                         IF window <> FrontWindow THEN
  1418.                             SelectWindow(window)
  1419.                         ELSE IF (gTerm <> NIL) THEN
  1420.                             TMClick(gTerm, event);
  1421.                     END;
  1422.                         
  1423.                 inDrag:        {pass screenBits.bounds to get all gDevices}
  1424.                     IF NOT DoToolEvent(event,window) THEN
  1425.                         DragWindow(window, event.where, screenBits.bounds);
  1426.                     
  1427.                 inGrow,
  1428.                 inZoomIn, inZoomOut,
  1429.                 inGoAway:
  1430.                     IF DoToolEvent(event,window) THEN ;
  1431.             END; { Case Mousedown    }
  1432.                 
  1433.         END; { Mousedown    }
  1434.         
  1435.         keyDown, autoKey: BEGIN                {check for menukey equivalents}
  1436.             window := FrontWindow;
  1437.             
  1438.             { Get the key    }
  1439.             key := CHR(BAnd(event.message, charCodeMask));
  1440.             processed := FALSE;
  1441.             
  1442.             { The terminal tool might be mapping the cmd key     }
  1443.             { so if menukey fails, send it to the tool            }
  1444.             
  1445.             IF BAND(event.modifiers, cmdKey) <> 0 THEN BEGIN
  1446.                 AdjustMenus;            {enable/disable/check menu items properly}
  1447.                 result := MenuKey(key);
  1448.                 IF result <> 0 THEN BEGIN
  1449.                     processed := TRUE;
  1450.                     DoMenuCommand(result)
  1451.                 END;
  1452.             END;
  1453.             
  1454.             IF (gTerm <> NIL) AND NOT processed THEN
  1455.                 IF NOT DoToolEvent(event,window) THEN
  1456.                     TMKey(gTerm, event);            
  1457.         END;
  1458.         
  1459.         activateEvt: BEGIN
  1460.             window := WindowPtr(event.message);
  1461.             
  1462.             IF NOT DoToolEvent(event,window) THEN
  1463.                 DoActivate(window, BAND(event.modifiers, activeFlag) <> 0);
  1464.         END;
  1465.         
  1466.         updateEvt: BEGIN
  1467.             window := WindowPtr(event.message);
  1468.             
  1469.             IF NOT DoToolEvent(event,window) THEN
  1470.                 DoUpdate(window);
  1471.         END;
  1472.         
  1473.         diskEvt:
  1474.             IF HiWrd(event.message) <> noErr THEN BEGIN
  1475.                 SetPt(aPoint, kDILeft, kDITop);
  1476.                 err := DIBadMount(aPoint, event.message);
  1477.             END;
  1478.             
  1479.         kOSEvent:
  1480.             { Send to frontmost tool window AND all tools    }
  1481.             { as this is an application-wide event            }
  1482.             
  1483.             CASE BAnd(BRotL(event.message, 8),$FF) OF    {high byte of message}
  1484.                 kSuspendResumeMessage: BEGIN
  1485.                     IF NOT DoToolEvent(event,FrontWindow) THEN
  1486.                         ;
  1487.                         
  1488.                     gInBackground := BAnd(event.message, kResumeMask) = 0;
  1489.                     DoResume(NOT gInBackground);
  1490.                 END;
  1491.             END;
  1492.     END;
  1493. END; {DoEvent}
  1494.  
  1495.  
  1496. { ******************************************************************
  1497. *    DoIdle    - Idles all the tools
  1498. *
  1499. ********************************************************************* }
  1500. {$S Main}
  1501. PROCEDURE DoIdle;
  1502. VAR
  1503.     theWindow     : WindowPtr;                { The target to idle        }
  1504.     doFT         : BOOLEAN;                    { route data to FT Tool        }
  1505.     doTM         : BOOLEAN;                    { route data to Term Tool    }
  1506.     savedPort    : GrafPtr;                    { for later reset            }
  1507.     status         : CMStatFlags;
  1508.     sizes         : CMBufferSizes;
  1509.     theErr        : CMErr;
  1510.     
  1511. BEGIN
  1512.     GetPort(savedPort);                        { Save for later            }
  1513.     theWindow := FrontWindow;                { Gimme the first one        }
  1514.     
  1515.     { Give idle time for the window    }
  1516.     WHILE (theWindow <> NIL) DO BEGIN
  1517.         IF IsAppWindow(theWindow) THEN BEGIN
  1518.             SetPort(theWindow);                    { Focus on it                }
  1519.             
  1520.             IF gConn <> NIL THEN BEGIN            { Give time to the connection    }
  1521.                 CMIdle(gConn);
  1522.             END;
  1523.                 
  1524.             doFT := FALSE;                    { Send data to FT tool            }
  1525.             doTM := TRUE;                    { Send data to terminal tool    }
  1526.             
  1527.             IF gFT <> NIL THEN BEGIN
  1528.                 { Is there a file transfer in progress ??    }
  1529.                 IF BAND(gFT^^.flags, ftIsFTMode) <> 0 THEN BEGIN
  1530.                     doFT := TRUE;
  1531.                     gWasFT := TRUE;
  1532.                     
  1533.                     { If the FT tool uses my connection then    }
  1534.                     { don't route data to the terminal tool        }
  1535.                     
  1536.                     IF BAND(gFT^^.attributes, ftSameCircuit) <> 0 THEN 
  1537.                         doTM := FALSE;
  1538.                 END    { In progress    }
  1539.                 
  1540.                 ELSE BEGIN
  1541.                     IF gWasFT THEN BEGIN
  1542.                         { FT no longer in progress        }
  1543.                         gWasFT := FALSE;
  1544.                         
  1545.                         { FT tool will alert the user    }
  1546.                         IF BAND(gFT^^.flags, FTSucc) = 0 THEN
  1547.                             ;
  1548.                         
  1549.                         { The old search was removed for the transfer    }
  1550.                         { so we need to re-add it here                    }
  1551.                         AddFTSearch;
  1552.                     END;
  1553.                                         
  1554.                     { AutoReceive string was received ?    }
  1555.                     IF gStartFT THEN
  1556.                         DoReceive;
  1557.                 END; { No FT in progress    }
  1558.                 
  1559.                 IF doFT THEN            { Give time to FT tool                }
  1560.                     FTExec(gFT);
  1561.                 
  1562.             END; { Good FT Handle    }
  1563.                                 
  1564.             IF gTerm <> NIL THEN BEGIN
  1565.                 TMIdle(gTerm);                    { So it can blink its cursor, etc    }
  1566.                 
  1567.                 IF doTM THEN        
  1568.                     TermRecvProc;                { Send Data to the terminal            }
  1569.             END; { Good Terminal    }
  1570.             
  1571.         END; { App Window    }
  1572.         
  1573.         { Try the next window    }
  1574.         theWindow := WindowPtr(WindowPeek(theWindow)^.nextWindow);
  1575.             
  1576.     END; { while each window    }
  1577.             
  1578.     SetPort(savedPort);                        { Back to the way it was        }
  1579.         
  1580. END; { DoIdle    }
  1581.  
  1582.  
  1583. { ******************************************************************
  1584. *    EventLoop    - The main event loop 
  1585. *
  1586. ********************************************************************* }
  1587. {$S Main}
  1588. PROCEDURE EventLoop;
  1589. VAR
  1590.     gotEvent    : BOOLEAN;
  1591.     event        : EventRecord;
  1592.  
  1593. BEGIN
  1594.     REPEAT
  1595.         DoIdle;
  1596.         
  1597.         IF gHasWaitNextEvent THEN     { put us 'asleep' forever under MultiFinder    }
  1598.             gotEvent := WaitNextEvent(everyEvent, event, 0, NIL)
  1599.         ELSE BEGIN
  1600.             SystemTask;                { must be called if using GetNextEvent        }
  1601.             gotEvent := GetNextEvent(everyEvent, event);
  1602.         END;
  1603.         
  1604.         IF gotEvent THEN BEGIN
  1605.             AdjustCursor(event.where);             {make sure we have the right cursor}
  1606.             DoEvent(event);
  1607.         END;
  1608.         
  1609.         AdjustCursor(event.where);
  1610.     UNTIL FALSE;                    {loop forever; we quit through an ExitToShell}
  1611. END; {EventLoop}
  1612.  
  1613.  
  1614. PROCEDURE _DataInit; EXTERNAL;
  1615.  
  1616.  
  1617. {$S Main}
  1618. BEGIN
  1619.     UnloadSeg(@_DataInit);    { note that _DataInit must not be in Main!            }
  1620.          
  1621.     MaxApplZone;            { expand the heap so code segments load at the top    }
  1622.  
  1623.     Initialize;                { initialize the program                            }
  1624.     UnloadSeg(@Initialize);    { note that Initialize must not be in Main!            }
  1625.  
  1626.     EventLoop;                { call the main event loop                            }
  1627. END.
  1628.